/**
 *
 * \file CGraphicsContext.cpp
 * \brief This file contains the CGraphicsContext class implementation.
 * \author Vladimir Klubkov (aka marked)
 *
 * Last changes date:
 * \date 15.03.2011
 *
 * Copyright 2011 Vladimir Klubkov <marked-one@yandex.ru>
 *
 * This file is part of OOXlib.
 *
 * OOXLib is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * OOXlib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OOXlib; if not, you can try to find  it here:
 * http://www.gnu.org/licenses/gpl.txt
 * or write to the Free Software Foundation, Inc., 59 Temple Place,
 * Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "../include/CGraphicsContext.h"

namespace OOXlib {

/**
 * Constructor. Calls the base-class constructor.
 * \param File Source code file name.
 * \param iLine Source code line.
 * \param Function Function name.
 */
CGraphicsContext::CNULLPtrIWindowException::CNULLPtrIWindowException(const ::std::string &File, const int iLine, const ::std::string &Function)
: CException(File, iLine, Function, "NULL pointer was specified as the IWindow object pointer.") {}
/**
 * Constructor. Calls the base-class constructor.
 * \param File Source code file name.
 * \param iLine Source code line.
 * \param Function Function name.
 */
CGraphicsContext::CXQueryFontException::CXQueryFontException(const ::std::string &File, const int iLine, const ::std::string &Function)
: CException(File, iLine, Function, "Can't obtain the font for current graphics context. The XQueryFont function failed.") {}
/**
 * Constructor.
 * \param pXServerConnection A pointer to the CXServerConnection object.
 * \param pWindow Pointer to the IWindow object.
 */
CGraphicsContext::CGraphicsContext(const CXServerConnection *pXServerConnection, const IWindow *pWindow)
: m_pXServerConnection(pXServerConnection), m_pWindow(pWindow) {
    if(NULL == m_pXServerConnection) {
        OOXLib_THROW(CNULLPtrConnectionException);
    }
    if(NULL == m_pWindow) {
        OOXLib_THROW(CNULLPtrIWindowException);
    }
    // Create the graphics context.
    ::XGCValues values;
    values.background = 1;
    // XCreateGC can generate BadAlloc, BadDrawable, BadFont, BadMatch, BadPixmap, and BadValue errors.
    m_GC = XCreateGC(m_pXServerConnection->Get(), m_pWindow->Get(), GCBackground, &values);
    m_Text.push_back("\0");
}
/**
 * Destructor.
 */
CGraphicsContext::~CGraphicsContext() {
    // XFreeGC can generate a BadGC error.
    ::XFreeGC(m_pXServerConnection->Get(), m_GC);
}
/**
 * Sets the foreground color.
 * \param Color CColor object.
 */
void CGraphicsContext::SetForegroundColor(const CColor *pColor) {
    // XSetForeground() can generate BadAlloc and BadGC errors.
    ::XSetForeground(m_pXServerConnection->Get(), m_GC, pColor->GetPixel());
}
/**
 * Sets the background color.
 * \param Color CColor object.
 */
void CGraphicsContext::SetBackgroundColor(const CColor *pColor) {
    // XSetBackground can generate BadAlloc and BadGC errors.
    ::XSetBackground(m_pXServerConnection->Get(), m_GC, pColor->GetPixel());
}
/**
 * Draws 2d line to the window.
 * \param Line CUInt2dLine object.
 */
void CGraphicsContext::DrawLine(const CUInt2dLine &Line) {
    // XDrawLine, can generate BadDrawable , BadGC , and BadMatch errors.
    ::XDrawLine(m_pXServerConnection->Get(), m_pWindow->Get(), m_GC, Line.GetStartX(), Line.GetStartY(), Line.GetEndX(), Line.GetEndY());
}
/**
 * Draws 2d rectangle to the window.
 * \param Rectangle CUInt2dRectangle object.
 */
void CGraphicsContext::DrawRectangle(const CUInt2dRectangle &Rectangle) {
    // XDrawRectangle can generate BadDrawable , BadGC , and BadMatch errors.
    ::XDrawRectangle(m_pXServerConnection->Get(), m_pWindow->Get(), m_GC, Rectangle.GetPositionX(), Rectangle.GetPositionY(), Rectangle.GetWidth(), Rectangle.GetHeight());
}
/**
 * Draws filled 2d rectangle to the window.
 * \param Rectangle CUInt2dRectangle object.
 */
void CGraphicsContext::DrawFilledRectangle(const CUInt2dRectangle &Rectangle) {
    // XFillRectangle can generate BadDrawable , BadGC , and BadMatch errors.
    ::XFillRectangle(m_pXServerConnection->Get(), m_pWindow->Get(), m_GC, Rectangle.GetPositionX(), Rectangle.GetPositionY(), Rectangle.GetWidth(), Rectangle.GetHeight());
}
/**
 * Sets the text.
 * \param Text Text to be drawn.
 */
void CGraphicsContext::SetText(const ::std::string &Text) {
    if(Text.empty()) {
        m_Text.clear();
        m_Text.push_back("\0");
    }
    size_t startPos = 0;
    size_t sepPos = 0;
    m_Text.clear();
    while(::std::string::npos != (sepPos = Text.find('\n', startPos))) {
        m_Text.push_back(Text.substr(startPos, sepPos - startPos));
        startPos = sepPos + 1;
    }
    m_Text.push_back(Text.substr(startPos));
}
/**
 * Computes text line height.
 * \return Text line height.
 */
unsigned int CGraphicsContext::ComputeTextLineHeight() {
    // Get the font from GC.
    ::XFontStruct *font = ::XQueryFont(m_pXServerConnection->Get(), ::XGContextFromGC(m_GC));
    // Compute and return the line height.
    if(NULL == font) {
        OOXLib_THROW(CXQueryFontException);
    }
    short height = font->max_bounds.ascent + font->max_bounds.descent;
    ::XFreeFontInfo(NULL, font, 0);
    return static_cast<unsigned int>(height);
}
/**
 * Computes width for every character for the text, which was specified by the SetText() method.
 * \return A vector, containing characters widths.
 */
::std::vector<unsigned int> CGraphicsContext::ComputeCharactersWidths() {
    // Get the font from GC.
    ::XFontStruct *font = ::XQueryFont(m_pXServerConnection->Get(), ::XGContextFromGC(m_GC));
    // Compute and return the line height.
    if(NULL == font) {
        OOXLib_THROW(CXQueryFontException);
    }
    // Compute character widths.
    ::std::vector<unsigned int> widths;
    size_t linesCount = m_Text.size();
    for(size_t i = 0u; i < linesCount; i++) {
        size_t lineLength = m_Text[i].size();
        for(size_t j = 0u; j < lineLength; j++) {
            widths.push_back(static_cast<unsigned int>(::XTextWidth(font, m_Text[i].c_str(), lineLength)));
        }
    }
    ::XFreeFontInfo(NULL, font, 0);
    return widths;
}
/**
 * Computes the text rectangle.
 * \return CUInt2dRectangle.
 */
CUInt2dRectangle CGraphicsContext::ComputeTextRectangle() {
    int direction = 0, fontAscent = 0, fontDescent = 0;
    ::XCharStruct charStruct;
    int width = 0;
    int height = 0;
    // Compute text width and height.
    size_t linesCount = m_Text.size();
    int lineHeight = static_cast<int>(ComputeTextLineHeight());
    for(size_t i = 0u; i < linesCount; i++) {
        size_t lineLength = m_Text[i].length();
        // XQueryTextExtents can generate BadFont and BadGC errors.
        ::XQueryTextExtents(m_pXServerConnection->Get(), ::XGContextFromGC(m_GC), m_Text[i].c_str(), lineLength, &direction, &fontAscent, &fontDescent, &charStruct);
        int currentWidth = charStruct.rbearing - charStruct.lbearing;
        if(currentWidth > width) {
            width = currentWidth;
        }
        height += lineHeight;
    }
    return CUInt2dRectangle(0, 0, static_cast<unsigned int>(width), static_cast<unsigned int>(height));
}
/**
 * Draws text to the window.
 * \param Position Position of the top left corner of the text rectangle.
 */
void CGraphicsContext::DrawText(const CUInt2dPoint &Position) {
    // Get line height from GC.
    int lineHeight = static_cast<int>(ComputeTextLineHeight());
    size_t linesCount = m_Text.size();
    // Draw text).
    for(size_t i = 0u; i < linesCount; i++) {
        size_t lineLength = m_Text[i].size();
        // XDrawString can generate BadDrawable , BadGC , and BadMatch errors.
        ::XDrawString(m_pXServerConnection->Get(), m_pWindow->Get(), m_GC, Position.GetX(), Position.GetY() + (lineHeight * (i + 1)), m_Text[i].c_str(), lineLength);
    }
}
/**
 * Draws pixmap to the window.
 * \param Pixmap Pixmap to draw.
 * \param Position Position of the top left corner of the pixmap.
 */
void CGraphicsContext::DrawPixmap(const CPixmap &Pixmap, const CUInt2dPoint &Position) {
    // XSetClipMask can generate BadAlloc, BadGC, BadMatch, and BadPixmap errors.
    ::XSetClipMask(m_pXServerConnection->Get(), m_GC, Pixmap.GetMask());
    // XSetClipOrigin can generate BadAlloc and BadGC errors.
    ::XSetClipOrigin(m_pXServerConnection->Get(), m_GC, Position.GetX(), Position.GetY());
    // XCopyArea() can generate BadDrawable, BadGC, and BadMatch errors.
    ::XCopyArea(m_pXServerConnection->Get(), Pixmap.Get(), m_pWindow->Get(), m_GC, 0, 0, Pixmap.GetWidth(), Pixmap.GetHeight(), Position.GetX(), Position.GetY());
    // XSetClipMask can generate BadAlloc, BadGC, BadMatch, and BadPixmap errors.
    ::XSetClipMask(m_pXServerConnection->Get(), m_GC, None);
}

} // OOXlib
